package com.oneinlet

import java.lang.reflect.Field
import java.util.*
import kotlin.collections.HashMap

/**
 * Created by WangZiHe on 2018/12/5/005
 * QQ/WeChat:648830605
 * QQ-Group:368512253
 * Blog:www.520code.net
 * Github:https://github.com/yancheng199287
 *
 *  警告：注意该工具类暂未支持内部类成员变量的属性值获取和复制,考虑判断基础数据类型，包装类型，各种集合类型
 */
object YCReflect {

    /**
     * 获取对象的所有属性和值,注意 如果对象的属性是null，获取该属性的值会报错，我们这里默认设置为null
     * @param any  接收一个对象
     * @return  返回一个map，包含该对象的所有键值对
     * */
    fun getPropertyValue(any: Any): Map<String, Any?> {
        val fields: Array<Field> = any::class.java.declaredFields
        val map = HashMap<String, Any?>(fields.size)
        for (field in fields) {
            field.isAccessible = true
            try {
                val value = field.get(any)
                map[field.name] = value
            } catch (e: Exception) {
                map[field.name] = null
            }
        }
        return map
    }

    /**
     *  根据对象属性名称获取该值
     * @param any  接收一个对象
     * @param name  属性名
     * @return  返回该对象属性名称对应的值，如果找不到该属性则返回null
     * */
    fun getValueByProperty(any: Any, name: String): Any? {
        val fields: Array<Field> = any::class.java.declaredFields
        for (field in fields) {
            if (field.name == name) {
                field.isAccessible = true
                return field.get(any)
            }
        }
        return null
    }


    /**
     *  调用对象的方法
     *  警告：如果传入的类存在重载的方法，且参数不一致，可能会导致错误的参数数量，解决办法，需要传递参数的类型和个数才能准确调用该方法
     * @param anyClass  接收一个Class对象
     * @param methodName  需要调用的方法名
     * @param parameters 调用方法需要传递的参数
     * */
    fun <T> callObjectMethod(anyClass: Class<T>, methodName: String, vararg parameters: Any) {
        val methods = anyClass.declaredMethods
        for (method in methods) {
            if (method.name == methodName) {
                method.invoke(anyClass.newInstance(), *parameters)
                return
            }
        }
    }

    /**
     *  为对象手动赋值给属性
     * @param any  接收一个对象
     * @param name  需要赋值的属性名称
     * @param value 需要赋值的值
     * @return  返回该对象属性名称对应的值，如果找不到该属性则返回null
     * */
    fun <T> setPropertyValue(any: Any, name: String, value: Any): T {
        val fields: Array<Field> = any::class.java.declaredFields
        for (field in fields) {
            if (field.name == name) {
                field.isAccessible = true
                field.set(any, value)
            }
        }
        return any as T
    }

    /**
     *  为对象手动赋值给属性
     * @param any  接收一个对象
     * @param map  该map包含第一个参数中对象的属性和值
     * @return  返回该对象属性名称对应的值，如果找不到该属性则返回null
     * */
    fun <T> setPropertyValue(any: Any, map: Map<String, Any>): T {
        val fields: Array<Field> = any::class.java.declaredFields
        for (field in fields) {
            field.isAccessible = true
            field.set(any, map[field.name])
        }
        return any as T
    }

    /**
     *  复制对象的属性的值到另外一个对象的属性值，排除null或者空字符串
     * @param any  接收一个对象  需要复制的属性对象
     * @param any  接收一个对象  被复制的属性对象
     * @return  返回复制完成属性值的other对象
     * */
    fun <T> copyObjectToOther(any: Any, other: Any): T {
        val map = getPropertyValue(any)
        val fields = getAllField(other)
        for (field in fields) {
            val value = map[field.name]
            if (value == null || value == "") {
                continue
            }
            field.isAccessible = true
            field.set(other, map[field.name])
        }
        return other as T
    }

    /***
     *  递归获取上一级类的所有成员属性，直到最顶层any
     *  @return  返回对象的所有属性和值，包括父类
     * **/
    private fun getAllField(any: Any): List<Field> {
        val fields = ArrayList<Field>()
        var classObject = any::class.java
        while (!classObject.isInstance(Any())) {
            fields.addAll(classObject.declaredFields.asList())
            classObject = classObject.superclass as Class<out Any>
        }
        return fields
    }


}